﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Data
{
    /// <summary>
    /// Provedor de dados
    /// </summary>
    public class DataProvider
    {
        /// <summary>
        /// Base de dados
        /// </summary>
        public Data[] DataSet 
        {
            get;
            private set; 
        }

        public long DataSetLines;
        public long DataSetColumns;
        public long InputsN;
        public long OutputsN;

        public Data[] TrainSet;
        public long TrainSetLines;
        public long TrainSetColumns;

        public Data[] ValidationSet;
        public long ValidationSetLines;
        public long ValidationSetColumns;

        public Data[] TestSet;
        public long TestSetLines;
        public long TestSetColumns;

        public double GreaterData;
        public double LowersData;

        public DataProvider(String filePath)
        {
            _ReadDataSetFromFile(filePath);
        }

        private void _ReadDataSetFromFile(String filePath)
        {
            String line = null;
            using (StreamReader reader = new StreamReader(filePath))
            {
		        line = reader.ReadLine();
	            DataSetLines = long.Parse(line); // número de exemplosos
		
		        line = reader.ReadLine();
		        DataSetColumns = long.Parse(line); // representa o numero de entradas + numero de saidas
		
		        line = reader.ReadLine();
		        InputsN = long.Parse(line);; // numero de entradas da rede
		
		        line = reader.ReadLine();
		        OutputsN = long.Parse(line);; // numero de saidas da rede
		
		        DataSet = new Data[DataSetLines];
	
		        for(int i = 0; i < DataSetLines; i++)
                {
			        line = reader.ReadLine();

			        String[] values = line.Split(',');

			        DataSet[i] = new Data(new double[InputsN], new double[OutputsN]);
			
			        for (int j = 0; j < values.Length; j++) 
                    {
					    double val = double.Parse(values[j]);
					
					    if (j >= InputsN)
						    DataSet[i].Output[j - InputsN] = val;
					    else
						    DataSet[i].Input[j] = val;	
			        }
		        }
            }
	    }

        public void PrintMatrix(Data[] d)
        {
		    int lin = d.Length;
		    int col = d[0].Input.Length + d[0].Output.Length;
		
		    Console.WriteLine("Número de linhas: " + lin);
		    Console.WriteLine("Número de colunas: " + col);
		    Console.WriteLine();
		
		    for(int i = 0; i < lin; i++)
            {
			    for(int j=0; j < d[0].Input.Length; j++)
                {
				    Console.Write("%.2f ", d[i].Input[j]);
			    }
			    for (int k = 0; k < d[0].Output.Length; k++)
                {
				    Console.Write("%.2f ", d[i].Output[k]);
			    }
	            Console.WriteLine();
		    }
	    }

        public void gerarOrdemAleatoriaDados(long semente)
        {
            GreaterData = 0;
            LowersData = double.MaxValue;

            List<Data> list = new List<Data>();

            for (int i = 0; i < DataSet.Length; i++)
            {
                list.Add(DataSet[i]);

                for (int j = 0; j < DataSet[0].Input.Length; j++)
                {
                    if (DataSet[i].Input[j] < LowersData)
                        LowersData = DataSet[i].Input[j];

                    if (DataSet[i].Input[j] > GreaterData)
                        GreaterData = DataSet[i].Input[j];
                }

                for (int k = 0; k < DataSet[0].Output.Length; k++)
                {
                    if (DataSet[i].Output[k] < LowersData)
                        LowersData = DataSet[i].Output[k];

                    if (DataSet[i].Output[k] > GreaterData)
                        GreaterData = DataSet[i].Output[k];
                }
            }
            list.Shuffle();
            //Collections.shuffle(list, new Random(semente));
           
            for (int i = 0; i < DataSet.Length; i++)
            {
                DataSet[i] = list[i];
            }
        }

        public void imprimirMaiorMenor()
	    {
		    Console.WriteLine("Maior:" + GreaterData);
		    Console.WriteLine("Menor:" + LowersData);
	    }

        public void normalizar()
        {
            for (int i = 0; i < DataSet.Length; i++)
            {
                for (int j = 0; j < DataSet[0].Input.Length; j++)
                {
                    DataSet[i].Input[j] = (0.7 * (DataSet[i].Input[j] - LowersData) / (GreaterData - LowersData)) + 0.15;
                }
                for (int k = 0; k < DataSet[0].Output.Length; k++)
                {
                    DataSet[i].Output[k] = (0.7 * (DataSet[i].Output[k] - LowersData) / (GreaterData - LowersData)) + 0.15;
                }
            }
        }
        public double desnormalizar(double dado)
        {
            return ((dado * GreaterData - 0.15) / 0.7) + LowersData;

        }

        public Data[] desnormalizar(Data[] dados)
        {
            Data[] result = new Data[dados.Length];

            for (int i = 0; i < dados.Length; i++)
            {
                result[i] = new Data(new double[dados[0].Input.Length], new double[dados[0].Output.Length]);

                for (int j = 0; j < dados[0].Input.Length; j++)
                {
                    result[i].Input[j] = ((dados[i].Input[j] * GreaterData - 0.15) / 0.7) + LowersData;
                }
                for (int k = 0; k < dados[0].Output.Length; k++)
                {
                    dados[i].Output[k] = ((dados[i].Output[k] * GreaterData - 0.15) / 0.7) + LowersData;
                }
            }
            return result;

        }
        public void dividirDados()
        {
            TrainSetLines = DataSetLines / 2; // 50% dos dados
            TestSetLines = ValidationSetLines = TrainSetLines / 2; // 25% dos dados

            ValidationSetColumns = TrainSetColumns = TestSetColumns = DataSetColumns; // numero de entradas + bias + saida

            TrainSet = new Data[TrainSetLines];
            ValidationSet = new Data[ValidationSetLines];
            TestSet = new Data[TestSetLines];

            for (int i = 0; i < TrainSetLines; i++)
            {
                for (int j = 0; j < TrainSetColumns; j++)
                {
                    TrainSet[i] = DataSet[i];
                }
            }

            for (long i = TrainSetLines, l = 0; i < TrainSetLines + ValidationSetLines; i++, l++)
            {
                for (int j = 0; j < this.ValidationSetColumns; j++)
                {
                    ValidationSet[l] = DataSet[i];
                }
            }

            for (long i = TrainSetLines + ValidationSetLines, l = 0; i < TrainSetLines + ValidationSetLines + TestSetLines; i++, l++)
            {
                for (int j = 0; j < TestSetColumns; j++)
                {
                    TestSet[l] = DataSet[i];
                }
            }
            this.DataSet = null;
        }
    }
}
